νμ μλ°μ€ν¬λ¦½νΈ μ€λ₯ 볡ꡬ ν¨ν΄μ λ°°μ보μΈμ. μ°μν μ±λ₯ μ ν(Graceful Degradation) κΈ°λ²μ λ§μ€ν°νμ¬, λ¬Έμ κ° λ°μν΄λ μμ μ μΌλ‘ μλνλ νλ ₯μ μ΄κ³ μ¬μ©μ μΉνμ μΈ μΉ μ ν리μΌμ΄μ μ ꡬμΆνλ λ°©λ²μ μλ΄ν©λλ€.
μλ°μ€ν¬λ¦½νΈ μ€λ₯ 볡ꡬ: μ°μν μ±λ₯ μ ν ꡬν ν¨ν΄ κ°μ΄λ
μΉ κ°λ°μ μΈκ³μμ μ°λ¦¬λ μλ²½μ μΆκ΅¬ν©λλ€. μ°λ¦¬λ κΉλν μ½λλ₯Ό μμ±νκ³ , ν¬κ΄μ μΈ ν μ€νΈλ₯Ό μννλ©°, μμ κ°μ κ°κ³ λ°°ν¬ν©λλ€. κ·Έλ¬λ μ΅μ μ λ Έλ ₯μλ λΆκ΅¬νκ³ , ν κ°μ§ 보νΈμ μΈ μ§μ€μ λ³νμ§ μμ΅λλ€: λ¬Έμ λ λ°λμ λ°μνλ€λ κ²μ λλ€. λ€νΈμν¬ μ°κ²°μ΄ λΆμμ ν΄μ§κ³ , APIκ° μλ΅νμ§ μμΌλ©°, μλνν° μ€ν¬λ¦½νΈκ° μ€ν¨νκ³ , μμμΉ λͺ»ν μ¬μ©μ μνΈμμ©μ΄ μ°λ¦¬κ° μμΈ‘νμ§ λͺ»ν μ£μ§ μΌμ΄μ€λ₯Ό μ λ°ν κ²μ λλ€. λ¬Έμ λ μ ν리μΌμ΄μ μ μ€λ₯κ° λ°μν μ§ μ¬λΆκ° μλλΌ, μ€λ₯κ° λ°μνμ λ μ΄λ»κ² λμν κ²μΈκ°μ λλ€.
ν λΉ ν° νλ©΄, κ³μν΄μ λμκ°λ λ‘λ, λλ μ μ μλ μ€λ₯ λ©μμ§λ λ¨μν λ²κ·Έ κ·Έ μ΄μμ λλ€. μ΄λ μ¬μ©μμμ μ λ’°λ₯Ό κΉ¨λ¨λ¦¬λ νμμ λλ€. λ°λ‘ μ΄ μ§μ μμ μ°μν μ±λ₯ μ ν(graceful degradation)μ μ€μ²μ΄ λͺ¨λ μ λ¬Έ κ°λ°μμκ² μ€μν κΈ°μ μ΄ λ©λλ€. μ΄λ μ΄μμ μΈ μ‘°κ±΄μμλ§ κΈ°λ₯νλ μ ν리μΌμ΄μ μ΄ μλλΌ, μΌλΆ κΈ°λ₯μ΄ μ€ν¨νλλΌλ νλ ₯μ μ΄κ³ μ¬μ© κ°λ₯ν μ ν리μΌμ΄μ μ ꡬμΆνλ κΈ°μ μ λλ€.
μ΄ ν¬κ΄μ μΈ κ°μ΄λμμλ μλ°μ€ν¬λ¦½νΈμ μ°μν μ±λ₯ μ νλ₯Ό μν μ€μ©μ μ΄κ³ ꡬν μ€μ¬μ μΈ ν¨ν΄μ νμν κ²μ λλ€. κΈ°λ³Έμ μΈ `try...catch`λ₯Ό λμ΄, λμ§νΈ νκ²½μ΄ μ΄λ€ μν©μ μ²νλλΌλ μ ν리μΌμ΄μ μ΄ μ¬μ©μλ₯Ό μν μ λ’°ν μ μλ λκ΅¬λ‘ λ¨μ μ μλλ‘ λ³΄μ₯νλ μ λ΅λ€μ κΉμ΄ νκ³ λ€ κ²μ λλ€.
μ°μν μ±λ₯ μ ν vs. μ μ§μ ν₯μ: μ€μν μ°¨μ΄μ
ν¨ν΄μ λν΄ μμ보기 μ μ, νν νΌλλλ μ§μ μ λͺ νν νλ κ²μ΄ μ€μν©λλ€. μ°μν μ±λ₯ μ νμ μ μ§μ ν₯μμ μ’ μ’ ν¨κ» μΈκΈλμ§λ§, μ΄ λμ κ°λ³μ±μ΄λΌλ λ¬Έμ μ μλ‘ λ°λ λ°©ν₯μμ μ κ·Όνλ λμ μ μλ©΄κ³Ό κ°μ΅λλ€.
- μ μ§μ ν₯μ(Progressive Enhancement): μ΄ μ λ΅μ λͺ¨λ λΈλΌμ°μ μμ μλνλ ν΅μ¬ μ½ν μΈ μ κΈ°λ₯μ κΈ°μ€μ μμ μμν©λλ€. κ·Έλ° λ€μ μ΄λ₯Ό μ§μν μ μλ λΈλΌμ°μ λ₯Ό μν΄ λ κ³ κΈ κΈ°λ₯κ³Ό νλΆν κ²½νμ κ³μΈ΅μ μΆκ°ν©λλ€. μ΄λ λκ΄μ μΈ μν₯μ(bottom-up) μ κ·Ό λ°©μμ λλ€.
- μ°μν μ±λ₯ μ ν(Graceful Degradation): μ΄ μ λ΅μ μμ νκ³ νλΆν κΈ°λ₯μ κ²½νμμ μμν©λλ€. κ·Έλ° λ€μ μ€ν¨λ₯Ό κ³ννκ³ , νΉμ κΈ°λ₯, API λλ 리μμ€λ₯Ό μ¬μ©ν μ μκ±°λ κΉ¨μ‘μ λ λ체 κΈ°λ₯μ΄λ ν΄λ°±(fallbacks)μ μ 곡ν©λλ€. μ΄λ νλ ₯μ±μ μ΄μ μ λ§μΆ μ€μ©μ μΈ νν₯μ(top-down) μ κ·Ό λ°©μμ λλ€.
μ΄ κΈμ μ°μν μ±λ₯ μ ν, μ¦ μ€ν¨λ₯Ό μμΈ‘νκ³ μ ν리μΌμ΄μ μ΄ λΆκ΄΄λμ§ μλλ‘ λ³΄μ₯νλ λ°©μ΄μ νμμ μ΄μ μ λ§μΆ₯λλ€. μ§μ μΌλ‘ κ²¬κ³ ν μ ν리μΌμ΄μ μ λ κ°μ§ μ λ΅μ λͺ¨λ μ¬μ©νμ§λ§, μΉμ μμΈ‘ λΆκ°λ₯ν νΉμ±μ μ²λ¦¬νλ λ°λ μ±λ₯ μ ν κΈ°λ²μ λ§μ€ν°νλ κ²μ΄ ν΅μ¬μ λλ€.
μλ°μ€ν¬λ¦½νΈ μ€λ₯μ μ ν μ΄ν΄νκΈ°
μ€λ₯λ₯Ό ν¨κ³Όμ μΌλ‘ μ²λ¦¬νλ €λ©΄ λ¨Όμ κ·Έ μμΈμ μ΄ν΄ν΄μΌ ν©λλ€. λλΆλΆμ νλ‘ νΈμλ μ€λ₯λ λͺ κ°μ§ μ£Όμ λ²μ£Όλ‘ λλ©λλ€:
- λ€νΈμν¬ μ€λ₯: κ°μ₯ νν μ€λ₯ μ€ νλμ λλ€. API μλν¬μΈνΈκ° λ€μ΄λκ±°λ, μ¬μ©μμ μΈν°λ· μ°κ²°μ΄ λΆμμ νκ±°λ, μμ² μκ°μ΄ μ΄κ³Όλ μ μμ΅λλ€. μ€ν¨ν `fetch()` νΈμΆμ΄ λνμ μΈ μμ λλ€.
- λ°νμ μ€λ₯: μλ°μ€ν¬λ¦½νΈ μ½λ μ체μ λ²κ·Έμ λλ€. νν μμΈμΌλ‘λ `TypeError`(μ: `Cannot read properties of undefined`), `ReferenceError`(μ: μ‘΄μ¬νμ§ μλ λ³μμ μ κ·Ό), λλ μΌκ΄μ± μλ μνλ‘ μ΄μ΄μ§λ λ‘μ§ μ€λ₯ λ±μ΄ μμ΅λλ€.
- μλνν° μ€ν¬λ¦½νΈ μ€ν¨: νλ μΉ μ±μ λΆμ, κ΄κ³ , κ³ κ° μ§μ μμ ― λ±μ μν΄ μλ§μ μΈλΆ μ€ν¬λ¦½νΈμ μμ‘΄ν©λλ€. μ΄λ¬ν μ€ν¬λ¦½νΈ μ€ νλκ° λ‘λμ μ€ν¨νκ±°λ λ²κ·Έλ₯Ό ν¬ν¨νκ³ μμΌλ©΄ λ λλ§μ μ°¨λ¨νκ±°λ μ 체 μ ν리μΌμ΄μ μ λ€μ΄μν€λ μ€λ₯λ₯Ό μ λ°ν μ μμ΅λλ€.
- νκ²½/λΈλΌμ°μ λ¬Έμ : μ¬μ©μκ° νΉμ μΉ APIλ₯Ό μ§μνμ§ μλ μ€λλ λΈλΌμ°μ λ₯Ό μ¬μ©νκ±°λ, λΈλΌμ°μ νμ₯ νλ‘κ·Έλ¨μ΄ μ ν리μΌμ΄μ μ½λμ μΆ©λν μ μμ΅λλ€.
μ΄λ¬ν λ²μ£Ό μ€ μ΄λ νλμμλΌλ μ²λ¦¬λμ§ μμ μ€λ₯λ μ¬μ©μ κ²½νμ μΉλͺ μ μΌ μ μμ΅λλ€. μ°μν μ±λ₯ μ νμ λͺ©νλ μ΄λ¬ν μ€ν¨μ νκΈ ν¨κ³Ό(blast radius)λ₯Ό μ΅μ νλ κ²μ λλ€.
κΈ°μ΄: `try...catch`λ₯Ό μ΄μ©ν λΉλκΈ° μ€λ₯ μ²λ¦¬
`try...catch...finally` λΈλ‘μ μ€λ₯ μ²λ¦¬ λꡬ ν€νΈμμ κ°μ₯ κΈ°λ³Έμ μΈ λꡬμ λλ€. νμ§λ§ μ ν΅μ μΈ κ΅¬ν λ°©μμ λκΈ°μ μ½λμμλ§ μλν©λλ€.
λκΈ°μ μμ :
try {
let data = JSON.parse(invalidJsonString);
// ... λ°μ΄ν° μ²λ¦¬
} catch (error) {
console.error("JSON νμ± μ€ν¨:", error);
// μ΄μ , μ°μνκ² μ±λ₯μ μ ν...
} finally {
// μ΄ μ½λλ μ€λ₯ λ°μ μ¬λΆμ μκ΄μμ΄ μ€νλ©λλ€. (μ: μ 리 μμ
)
}
νλ μλ°μ€ν¬λ¦½νΈμμ λλΆλΆμ I/O μμ μ μ£Όλ‘ Promiseλ₯Ό μ¬μ©νλ λΉλκΈ° λ°©μμ λλ€. μ΄λ₯Ό μν΄ μ€λ₯λ₯Ό μ‘λ λ κ°μ§ μ£Όμ λ°©λ²μ΄ μμ΅λλ€:
1. Promiseμ `.catch()` λ©μλ:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => { /* λ°μ΄ν°λ₯Ό μ¬μ© */ })
.catch(error => {
console.error("API νΈμΆ μ€ν¨:", error);
// μ¬κΈ°μ ν΄λ°± λ‘μ§μ ꡬνν©λλ€
});
2. `async/await`μ ν¨κ» `try...catch` μ¬μ©:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP μ€λ₯! status: ${response.status}`);
}
const data = await response.json();
// λ°μ΄ν°λ₯Ό μ¬μ©
} catch (error) {
console.error("λ°μ΄ν° κ°μ Έμ€κΈ° μ€ν¨:", error);
// μ¬κΈ°μ ν΄λ°± λ‘μ§μ ꡬνν©λλ€
}
}
μ΄λ¬ν κΈ°λ³Έ μ¬νμ λ§μ€ν°νλ κ²μ μ΄μ΄μ§λ λ κ³ κΈ ν¨ν΄μ ꡬννκΈ° μν μ μ 쑰건μ λλ€.
ν¨ν΄ 1: μ»΄ν¬λνΈ μμ€ ν΄λ°± (μλ¬ λ°μ΄λ리)
μ΅μ μ μ¬μ©μ κ²½ν μ€ νλλ μκ³ μ€μνμ§ μμ UI μΌλΆκ° μ€ν¨νμ¬ μ 체 μ ν리μΌμ΄μ μ λ€μ΄μν€λ κ²μ λλ€. ν΄κ²°μ± μ μ»΄ν¬λνΈλ₯Ό 격리νμ¬ νλμ μ€λ₯κ° λ€λ₯Έ λͺ¨λ κ²μ μ°μμ μΌλ‘ μν₯μ λ―Έμ³ μΆ©λμ μΌμΌν€μ§ μλλ‘ νλ κ²μ λλ€. μ΄ κ°λ μ 리μ‘νΈμ κ°μ νλ μμν¬μμ "μλ¬ λ°μ΄λ리(Error Boundaries)"λ‘ μ λͺ νκ² κ΅¬νλμμ΅λλ€.
κ·Έλ¬λ μ΄ μμΉμ 보νΈμ μ λλ€: κ°λ³ μ»΄ν¬λνΈλ₯Ό μ€λ₯ μ²λ¦¬ κ³μΈ΅μΌλ‘ κ°μΈλ κ²μ λλ€. λ§μ½ μ»΄ν¬λνΈκ° λ λλ§μ΄λ μλͺ μ£ΌκΈ° λμ μ€λ₯λ₯Ό λ°μμν€λ©΄, μ΄ κ²½κ³κ° μ€λ₯λ₯Ό μ‘μλ΄κ³ λμ ν΄λ°± UIλ₯Ό νμν©λλ€.
μμ μλ°μ€ν¬λ¦½νΈμμμ ꡬν
λͺ¨λ UI μ»΄ν¬λνΈμ λ λλ§ λ‘μ§μ κ°μΈλ κ°λ¨ν ν¨μλ₯Ό λ§λ€ μ μμ΅λλ€.
function createErrorBoundary(componentElement, renderFunction) {
try {
// μ»΄ν¬λνΈμ λ λλ§ λ‘μ§ μ€ν μλ
renderFunction();
} catch (error) {
console.error(`μ»΄ν¬λνΈ μ€λ₯: ${componentElement.id}`, error);
// μ°μν μ±λ₯ μ ν: ν΄λ°± UI λ λλ§
componentElement.innerHTML = `<div class="error-fallback">
<p>μ£μ‘ν©λλ€. μ΄ μΉμ
μ λ‘λν μ μμ΅λλ€.</p>
</div>`;
}
}
μ¬μ© μμ : λ μ¨ μμ ―
λ€μν μ΄μ λ‘ μ€ν¨ν μ μλ λ μ¨ λ°μ΄ν°λ₯Ό κ°μ Έμ€λ μμ ―μ΄ μλ€κ³ μμν΄ λ³΄μΈμ.
const weatherWidget = document.getElementById('weather-widget');
createErrorBoundary(weatherWidget, () => {
// μλμ, μ μ¬μ μΌλ‘ μ·¨μ½ν λ λλ§ λ‘μ§
const weatherData = getWeatherData(); // μ¬κΈ°μ μ€λ₯κ° λ°μν μ μμ
if (!weatherData) {
throw new Error("λ μ¨ λ°μ΄ν°λ₯Ό μ¬μ©ν μ μμ΅λλ€.");
}
weatherWidget.innerHTML = `<h3>νμ¬ λ μ¨</h3><p>${weatherData.temp}Β°C</p>`;
});
μ΄ ν¨ν΄μ μ¬μ©νλ©΄, `getWeatherData()`κ° μ€ν¨νλλΌλ μ€ν¬λ¦½νΈ μ€νμ΄ μ€λ¨λλ λμ , μ¬μ©μλ μμ ― μ리μ μ μ€ν λ©μμ§λ₯Ό λ³΄κ² λλ©°, μ ν리μΌμ΄μ μ λλ¨Έμ§ λΆλΆ(λ©μΈ λ΄μ€ νΌλ, λ΄λΉκ²μ΄μ λ±)μ μλ²½νκ² κ³μ μλν©λλ€.
ν¨ν΄ 2: νΌμ² νλκ·Έλ₯Ό μ΄μ©ν κΈ°λ₯ μμ€ μ ν
νΌμ² νλκ·Έ(λλ ν κΈ)λ μλ‘μ΄ κΈ°λ₯μ μ μ§μ μΌλ‘ μΆμνλ λ° κ°λ ₯ν λꡬμ λλ€. λν μ€λ₯ 볡ꡬλ₯Ό μν νλ₯ν λ©μ»€λμ¦μΌλ‘λ μλν©λλ€. μλ‘κ±°λ 볡μ‘ν κΈ°λ₯μ νλκ·Έλ‘ κ°μΈλ©΄, μ 체 μ ν리μΌμ΄μ μ μ¬λ°°ν¬ν νμ μμ΄ νλ‘λμ μμ λ¬Έμ κ° λ°μνκΈ° μμν λ μ격μΌλ‘ λΉνμ±νν μ μλ λ₯λ ₯μ μ»κ² λ©λλ€.
μ€λ₯ 볡ꡬλ₯Ό μν΄ μλνλ λ°©μ:
- μ격 ꡬμ±: μ ν리μΌμ΄μ μ μμ μ λͺ¨λ νΌμ² νλκ·Έμ μνλ₯Ό ν¬ν¨νλ κ΅¬μ± νμΌμ κ°μ Έμ΅λλ€ (μ: `{"isLiveChatEnabled": true, "isNewDashboardEnabled": false}`).
- μ‘°κ±΄λΆ μ΄κΈ°ν: μ½λλ κΈ°λ₯μ μ΄κΈ°ννκΈ° μ μ νλκ·Έλ₯Ό νμΈν©λλ€.
- λ‘컬 ν΄λ°±: κ²¬κ³ ν λ‘컬 ν΄λ°±μ μν΄ μ΄λ₯Ό `try...catch` λΈλ‘κ³Ό κ²°ν©ν μ μμ΅λλ€. κΈ°λ₯μ μ€ν¬λ¦½νΈκ° μ΄κΈ°νμ μ€ν¨νλ©΄ νλκ·Έκ° κΊΌμ§ κ²μ²λΌ μ²λ¦¬ν μ μμ΅λλ€.
μμ : μλ‘μ΄ λΌμ΄λΈ μ±ν κΈ°λ₯
// μλΉμ€μμ κ°μ Έμ¨ νΌμ² νλκ·Έ
const featureFlags = { isLiveChatEnabled: true };
function initializeChat() {
if (featureFlags.isLiveChatEnabled) {
try {
// μ±ν
μμ ―μ μν 볡μ‘ν μ΄κΈ°ν λ‘μ§
const chatSDK = new ThirdPartyChatSDK({ apiKey: '...' });
chatSDK.render('#chat-container');
} catch (error) {
console.error("λΌμ΄λΈ μ±ν
SDK μ΄κΈ°ν μ€ν¨.", error);
// μ°μν μ±λ₯ μ ν: λμ 'λ¬ΈμνκΈ°' λ§ν¬ νμ
document.getElementById('chat-container').innerHTML =
'<a href="/contact">λμμ΄ νμνμ κ°μ? λ¬ΈμνκΈ°</a>';
}
}
}
μ΄ μ κ·Ό λ°©μμ λ λ¨κ³μ λ°©μ΄λ₯Ό μ 곡ν©λλ€. λ°°ν¬ ν μ±ν SDKμμ μ£Όμ λ²κ·Έλ₯Ό λ°κ²¬νλ©΄ κ΅¬μ± μλΉμ€μμ `isLiveChatEnabled` νλκ·Έλ₯Ό `false`λ‘ λ³κ²½νκΈ°λ§ νλ©΄ λͺ¨λ μ¬μ©μκ° μ¦μ κΉ¨μ§ κΈ°λ₯ λ‘λλ₯Ό μ€λ¨ν©λλ€. λν, νΉμ μ¬μ©μμ λΈλΌμ°μ κ° SDKμ λ¬Έμ κ° μλ κ²½μ°, `try...catch`κ° μ 체 μλΉμ€ κ°μ μμ΄ ν΄λΉ μ¬μ©μμ κ²½νμ κ°λ¨ν λ¬Έμ λ§ν¬λ‘ μ°μνκ² μ νμν΅λλ€.
ν¨ν΄ 3: λ°μ΄ν° λ° API ν΄λ°±
μ ν리μΌμ΄μ μ APIμ λ°μ΄ν°μ ν¬κ² μμ‘΄νλ―λ‘ λ°μ΄ν° κ°μ Έμ€κΈ° κ³μΈ΅μμμ κ²¬κ³ ν μ€λ₯ μ²λ¦¬λ ννν μ μλ λΆλΆμ λλ€. API νΈμΆμ΄ μ€ν¨νμ λ, κΉ¨μ§ μνλ₯Ό 보μ¬μ£Όλ κ²μ μ΅μ μ μ νμ λλ€. λμ , λ€μ μ λ΅λ€μ κ³ λ €ν΄ λ³΄μΈμ.
νμ ν¨ν΄: μ€λλ/μΊμλ λ°μ΄ν° μ¬μ©
μ΅μ λ°μ΄ν°λ₯Ό μ»μ μ μλ€λ©΄, μ½κ° μ€λλ λ°μ΄ν°κ° μ°¨μ μ± μΈ κ²½μ°κ° λ§μ΅λλ€. `localStorage`λ μλΉμ€ μ컀λ₯Ό μ¬μ©νμ¬ μ±κ³΅μ μΈ API μλ΅μ μΊμν μ μμ΅λλ€.
async function getAccountDetails() {
const cacheKey = 'accountDetailsCache';
try {
const response = await fetch('/api/account');
const data = await response.json();
// μ±κ³΅μ μΈ μλ΅μ νμμ€ν¬νμ ν¨κ» μΊμ
localStorage.setItem(cacheKey, JSON.stringify({ data, timestamp: Date.now() }));
return data;
} catch (error) {
console.warn("API κ°μ Έμ€κΈ° μ€ν¨. μΊμ μ¬μ© μλ.");
const cached = localStorage.getItem(cacheKey);
if (cached) {
// μ€μ: μ¬μ©μμκ² λ°μ΄ν°κ° μ€μκ°μ΄ μλμ μλ¦Ό!
showToast("μΊμλ λ°μ΄ν°λ₯Ό νμ μ€μ
λλ€. μ΅μ μ 보λ₯Ό κ°μ Έμ¬ μ μμ΅λλ€.");
return JSON.parse(cached).data;
}
// μΊμκ° μλ€λ©΄, μ€λ₯λ₯Ό μμμμ μ²λ¦¬νλλ‘ λμ ΈμΌ ν¨.
throw new Error("APIμ μΊμ λͺ¨λ μ¬μ©ν μ μμ΅λλ€.");
}
}
νμ ν¨ν΄: κΈ°λ³Έ λλ λͺ¨μ λ°μ΄ν°
νμμ μ΄μ§ μμ UI μμμ κ²½μ°, μ€λ₯λ λΉ κ³΅κ°μ 보μ¬μ£Όλ κ²λ³΄λ€ κΈ°λ³Έ μνλ₯Ό 보μ¬μ£Όλ κ²μ΄ λ λμ μ μμ΅λλ€. μ΄λ κ°μΈνλ μΆμ²μ΄λ μ΅κ·Ό νλ νΌλμ κ°μ κΈ°λ₯μ νΉν μ μ©ν©λλ€.
async function getRecommendedProducts() {
try {
const response = await fetch('/api/recommendations');
return await response.json();
} catch (error) {
console.error("μΆμ² μνμ κ°μ Έμ¬ μ μμ΅λλ€.", error);
// μΌλ°μ μ΄κ³ κ°μΈνλμ§ μμ λͺ©λ‘μΌλ‘ ν΄λ°±
return [
{ id: 'p1', name: 'λ² μ€νΈμ
λ¬ μν A' },
{ id: 'p2', name: 'μΈκΈ° μν B' }
];
}
}
νμ ν¨ν΄: μ§μμ λ°±μ€νλ₯Ό μ΄μ©ν API μ¬μλ λ‘μ§
λλ‘λ λ€νΈμν¬ μ€λ₯κ° μΌμμ μΌ μ μμ΅λλ€. κ°λ¨ν μ¬μλλ‘ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ΅λλ€. κ·Έλ¬λ μ¦μ μ¬μλνλ©΄ μ΄λ €μμ κ²ͺκ³ μλ μλ²μ κ³ΌλΆνλ₯Ό μ€ μ μμ΅λλ€. κ°μ₯ μ’μ λ°©λ²μ "μ§μμ λ°±μ€ν(exponential backoff)"λ₯Ό μ¬μ©νλ κ²μ λλ€. μ¦, μ¬μλν λλ§λ€ μ μ§μ μΌλ‘ λ κΈ΄ μκ°μ κΈ°λ€λ¦¬λ κ²μ λλ€.
async function fetchWithRetry(url, options, retries = 3, delay = 1000) {
try {
return await fetch(url, options);
} catch (error) {
if (retries > 0) {
console.log(`${delay}ms νμ μ¬μλν©λλ€... (λ¨μ μ¬μλ νμ: ${retries})`);
await new Promise(resolve => setTimeout(resolve, delay));
// λ€μ μ μ¬μ μ¬μλλ₯Ό μν΄ μ§μ° μκ°μ λ λ°°λ‘ λλ¦Ό
return fetchWithRetry(url, options, retries - 1, delay * 2);
} else {
// λͺ¨λ μ¬μλ μ€ν¨, μ΅μ’
μ€λ₯λ₯Ό λμ§
throw new Error("μ¬λ¬ λ²μ μ¬μλ ν API μμ² μ€ν¨.");
}
}
}
ν¨ν΄ 4: λ κ°μ²΄ ν¨ν΄ (Null Object Pattern)
`TypeError`μ λΉλ²ν μμΈ μ€ νλλ `null`μ΄λ `undefined`μ μμ±μ μ κ·Όνλ €κ³ μλνλ κ²μ λλ€. μ΄λ APIμμ λ°μμ¬ κ²μΌλ‘ μμνλ κ°μ²΄κ° λ‘λμ μ€ν¨νμ λ μμ£Ό λ°μν©λλ€. λ κ°μ²΄ ν¨ν΄μ μ΄ λ¬Έμ λ₯Ό ν΄κ²°νλ κ³ μ μ μΈ λμμΈ ν¨ν΄μΌλ‘, μμλ μΈν°νμ΄μ€λ₯Ό λ°λ₯΄μ§λ§ μ€λ¦½μ μ΄κ³ μ무 μμ λ νμ§ μλ(no-op) λμμ νλ νΉλ³ν κ°μ²΄λ₯Ό λ°νν©λλ€.
ν¨μκ° `null`μ λ°ννλ λμ , μ½λλ₯Ό μ¬μ©νλ μͺ½μμ μ€λ₯λ₯Ό μΌμΌν€μ§ μμ κΈ°λ³Έ κ°μ²΄λ₯Ό λ°ννλ κ²μ λλ€.
μμ : μ¬μ©μ νλ‘ν
λ κ°μ²΄ ν¨ν΄ λ―Έμ¬μ© μ (μ·¨μ½ν¨):
async function getUser(id) {
try {
// ... μ¬μ©μ μ 보 κ°μ Έμ€κΈ°
return user;
} catch (error) {
return null; // μ΄κ²μ μνν©λλ€!
}
}
const user = await getUser(123);
// getUserκ° μ€ν¨νλ©΄, μ΄ μ½λλ "TypeError: Cannot read properties of null (reading 'name')" μ€λ₯λ₯Ό λ°μμν΄
document.getElementById('welcome-banner').textContent = `νμν©λλ€, ${user.name}λ!`;
λ κ°μ²΄ ν¨ν΄ μ¬μ© μ (νλ ₯μ ):
const createGuestUser = () => ({
name: 'λ°©λ¬Έμ',
isLoggedIn: false,
permissions: [],
getAvatarUrl: () => '/images/default-avatar.png'
});
async function getUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) return createGuestUser();
return await response.json();
} catch (error) {
return createGuestUser(); // μ€ν¨ μ κΈ°λ³Έ κ°μ²΄ λ°ν
}
}
const user = await getUser(123);
// μ΄ μ½λλ μ΄μ API νΈμΆμ΄ μ€ν¨ν΄λ μμ νκ² μλν©λλ€.
document.getElementById('welcome-banner').textContent = `νμν©λλ€, ${user.name}λ!`;
if (!user.isLoggedIn) { /* λ‘κ·ΈμΈ λ²νΌ νμ */ }
μ΄ ν¨ν΄μ λ μ΄μ null 체ν¬(`if (user && user.name)`)λ‘ μ½λλ₯Ό μ΄μ§λ½ν νμκ° μμΌλ―λ‘ μ¬μ©νλ μ½λλ₯Ό λ§€μ° λ¨μνν©λλ€.
ν¨ν΄ 5: μ νμ κΈ°λ₯ λΉνμ±ν
λλ‘λ κΈ°λ₯ μ 체λ μλνμ§λ§ κ·Έ μμ νΉμ νμ κΈ°λ₯μ΄ μ€ν¨νκ±°λ μ§μλμ§ μμ μ μμ΅λλ€. μ 체 κΈ°λ₯μ λΉνμ±ννλ λμ , λ¬Έμ κ° λλ λΆλΆλ§ μΈκ³Όμ μΌλ‘ λΉνμ±νν μ μμ΅λλ€.
μ΄λ μ’ μ’ κΈ°λ₯ κ°μ§(feature detection)μ κ΄λ ¨μ΄ μμ΅λλ€. μ¦, λΈλΌμ°μ APIλ₯Ό μ¬μ©νκΈ° μ μ μ¬μ© κ°λ₯νμ§ νμΈνλ κ²μ λλ€.
μμ : λ¦¬μΉ ν μ€νΈ μλν°
μ΄λ―Έμ§λ₯Ό μ λ‘λνλ λ²νΌμ΄ μλ ν μ€νΈ μλν°λ₯Ό μμν΄ λ³΄μΈμ. μ΄ λ²νΌμ νΉμ API μλν¬μΈνΈμ μμ‘΄ν©λλ€.
// μλν° μ΄κΈ°ν μ€
const imageUploadButton = document.getElementById('image-upload-btn');
fetch('/api/upload-status')
.then(response => {
if (!response.ok) {
// μ
λ‘λ μλΉμ€κ° λ€μ΄λ¨. λ²νΌμ λΉνμ±ν.
imageUploadButton.disabled = true;
imageUploadButton.title = 'μ΄λ―Έμ§ μ
λ‘λλ₯Ό μΌμμ μΌλ‘ μ¬μ©ν μ μμ΅λλ€.';
}
})
.catch(() => {
// λ€νΈμν¬ μ€λ₯, μμ λΉνμ±ν.
imageUploadButton.disabled = true;
imageUploadButton.title = 'μ΄λ―Έμ§ μ
λ‘λλ₯Ό μΌμμ μΌλ‘ μ¬μ©ν μ μμ΅λλ€.';
});
μ΄ μλ리μ€μμ μ¬μ©μλ μ¬μ ν ν μ€νΈλ₯Ό μμ±νκ³ μμμ μ§μ νλ©°, μμ μ μ μ₯νκ³ , μλν°μ λ€λ₯Έ λͺ¨λ κΈ°λ₯μ μ¬μ©ν μ μμ΅λλ€. μ°λ¦¬λ νμ¬ κΉ¨μ§ κΈ°λ₯ νλλ§ μ κ±°ν¨μΌλ‘μ¨ λꡬμ ν΅μ¬ μ μ©μ±μ 보쑴νλ©΄μ κ²½νμ μ°μνκ² μ νμμΌ°μ΅λλ€.
λ λ€λ₯Έ μλ λΈλΌμ°μ κΈ°λ₯ νμΈμ λλ€:
const copyButton = document.getElementById('copy-text-btn');
if (!navigator.clipboard || !navigator.clipboard.writeText) {
// Clipboard APIκ° μ§μλμ§ μμ. λ²νΌμ μ¨κΉ.
copyButton.style.display = 'none';
} else {
// μ΄λ²€νΈ 리μ€λ μ°κ²°
copyButton.addEventListener('click', copyTextToClipboard);
}
λ‘κΉ λ° λͺ¨λν°λ§: 볡ꡬμ κΈ°λ°
μ‘΄μ¬νλμ§ λͺ¨λ₯΄λ μ€λ₯λ‘λΆν° μ°μνκ² μ±λ₯μ μ νμν¬ μλ μμ΅λλ€. μμμ λ Όμλ λͺ¨λ ν¨ν΄μ κ²¬κ³ ν λ‘κΉ μ λ΅κ³Ό λ³νλμ΄μΌ ν©λλ€. `catch` λΈλ‘μ΄ μ€νλ λ, μ¬μ©μμκ² ν΄λ°±μ 보μ¬μ£Όλ κ²λ§μΌλ‘λ μΆ©λΆνμ§ μμ΅λλ€. νμ΄ λ¬Έμ λ₯Ό μΈμ§ν μ μλλ‘ μ격 μλΉμ€μ μ€λ₯λ₯Ό κΈ°λ‘ν΄μΌ ν©λλ€.
μ μ μ€λ₯ νΈλ€λ¬ ꡬν
νλ μ ν리μΌμ΄μ μ μ μ© μ€λ₯ λͺ¨λν°λ§ μλΉμ€(Sentry, LogRocket, Datadog λ±)λ₯Ό μ¬μ©ν΄μΌ ν©λλ€. μ΄λ¬ν μλΉμ€λ ν΅ν©νκΈ° μ½κ³ λ¨μν `console.error`λ³΄λ€ ν¨μ¬ λ λ§μ 컨ν μ€νΈλ₯Ό μ 곡ν©λλ€.
λν νΉμ `try...catch` λΈλ‘μ ν΅κ³Όν λͺ¨λ μ€λ₯λ₯Ό μ‘κΈ° μν΄ μ μ νΈλ€λ¬λ₯Ό ꡬνν΄μΌ ν©λλ€.
// λκΈ°μ μ€λ₯ λ° μ²λ¦¬λμ§ μμ μμΈμ κ²½μ°
window.onerror = function(message, source, lineno, colno, error) {
// μ΄ λ°μ΄ν°λ₯Ό λ‘κΉ
μλΉμ€λ‘ μ μ‘
ErrorLoggingService.log({
message,
source,
lineno,
stack: error ? error.stack : null
});
// κΈ°λ³Έ λΈλΌμ°μ μ€λ₯ μ²λ¦¬(μ: μ½μ λ©μμ§)λ₯Ό λ°©μ§νλ €λ©΄ trueλ₯Ό λ°ν
return true;
};
// μ²λ¦¬λμ§ μμ νλ‘λ―Έμ€ κ±°λΆμ κ²½μ°
window.addEventListener('unhandledrejection', event => {
ErrorLoggingService.log({
reason: event.reason.message,
stack: event.reason.stack
});
});
μ΄λ¬ν λͺ¨λν°λ§μ μ€μν νΌλλ°± 루νλ₯Ό λ§λλλ€. μ΄λ₯Ό ν΅ν΄ μ΄λ€ μ±λ₯ μ ν ν¨ν΄μ΄ κ°μ₯ μμ£Ό νΈλ¦¬κ±°λλμ§ νμΈνκ³ , κ·Όλ³Έμ μΈ λ¬Έμ μ λν μμ μ°μ μμλ₯Ό μ νλ©°, μκ°μ΄ μ§λ¨μ λ°λΌ λμ± νλ ₯μ μΈ μ ν리μΌμ΄μ μ ꡬμΆνλ λ° λμμ΄ λ©λλ€.
κ²°λ‘ : νλ ₯μ±μ λ¬Έν ꡬμΆ
μ°μν μ±λ₯ μ νλ λ¨μν μ½λ© ν¨ν΄μ μ§ν© κ·Έ μ΄μμ λλ€. κ·Έκ²μ μ¬κ³ λ°©μμ λλ€. μ΄λ λ°©μ΄μ νλ‘κ·Έλλ°μ μ€μ²μ΄λ©°, λΆμ° μμ€ν μ λ΄μ¬λ μ·¨μ½μ±μ μΈμ νκ³ , 무μλ³΄λ€ μ¬μ©μμ κ²½νμ μ°μ μνλ κ²μ λλ€.
λ¨μν `try...catch`λ₯Ό λμ΄ λ€μΈ΅μ μΈ μ λ΅μ μ±νν¨μΌλ‘μ¨, μ€νΈλ μ€ μν©μμ μ ν리μΌμ΄μ μ λμμ λ³νμν¬ μ μμ΅λλ€. 첫 λ¬Έμ μ μ§νμ μ°μ°μ‘°κ° λλ μ·¨μ½ν μμ€ν λμ , λ¬Έμ κ° λ°μνμ λμλ ν΅μ¬ κ°μΉλ₯Ό μ μ§νκ³ μ¬μ©μ μ λ’°λ₯Ό μ μ§νλ νλ ₯μ μ΄κ³ μ μλ ₯ μλ κ²½νμ λ§λ€ μ μμ΅λλ€.
μ ν리μΌμ΄μ μμ κ°μ₯ μ€μν μ¬μ©μ μ¬μ μ μλ³νλ κ²λΆν° μμνμΈμ. μ΄λμμ μ€λ₯κ° κ°μ₯ ν° νΌν΄λ₯Ό μ€κΉμ? λ€μ ν¨ν΄λ€μ κ·Έκ³³μ λ¨Όμ μ μ©νμΈμ:
- 격리νμΈμ: μλ¬ λ°μ΄λλ¦¬λ‘ μ»΄ν¬λνΈλ₯Ό 격리ν©λλ€.
- μ μ΄νμΈμ: νΌμ² νλκ·Έλ‘ κΈ°λ₯μ μ μ΄ν©λλ€.
- μμΈ‘νμΈμ: μΊμ±, κΈ°λ³Έκ°, μ¬μλλ₯Ό ν΅ν΄ λ°μ΄ν° μ€ν¨λ₯Ό μμΈ‘ν©λλ€.
- λ°©μ§νμΈμ: λ κ°μ²΄ ν¨ν΄μΌλ‘ νμ μ€λ₯λ₯Ό λ°©μ§ν©λλ€.
- λΉνμ±ννμΈμ: μ 체 κΈ°λ₯μ΄ μλ, κΉ¨μ§ λΆλΆλ§ λΉνμ±νν©λλ€.
- λͺ¨λν°λ§νμΈμ: νμ λͺ¨λ κ²μ λͺ¨λν°λ§ν©λλ€.
μ€ν¨λ₯Ό λλΉνμ¬ κ΅¬μΆνλ κ²μ λΉκ΄μ μΈ κ²μ΄ μλλΌ μ λ¬Έμ μΈ κ²μ λλ€. μ΄κ²μ΄ λ°λ‘ μ¬μ©μκ° λ§λ ν λ°μμΌ ν κ²¬κ³ νκ³ , μ λ’°ν μ μμΌλ©°, μ‘΄μ€νλ μΉ μ ν리μΌμ΄μ μ ꡬμΆνλ λ°©λ²μ λλ€.